package server;
import java.io.*;
import java.util.Vector;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Date;
import java.text.*;
import java.lang.*;

/**
 * <p>Title: Response.java</p>
 *
 * <p>Description: Used to process incoming requests to the server and generate
 * the appropriate response to that request. This is where most of the server
 * processing is done, thus making it a very important class to the
 * implementation of your server.</p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: </p>
 * @author Tracie Hong and Luping May
 * @version 1.0
 */
public class Response {
    private int statusCode=0;
    private String FileExtension;
    private String URI;
    private File outFile;
    private int outFileSize;
    private Date time;
    private String date;
    private String version;
    private String firstOutputLine;
    private Request IncomingRequest;
    //private BufferedInputStream out;
    private InputStream out;
    private boolean runningScript;
    private Authentication authentication = new Authentication();
    private Vector<String> moreHeaders = new Vector<String>();

  /**
   * Default constructor for the response object. Variables are reset and/or
   * intialized here. These variables will be used throughout request processing
   * and response generation.
   */
  public Response() {
      FileExtension = "";
      URI = "";
      time = new Date();
      String[] temp = time.toString().split(" ");
      date = temp[2]+" "+temp[1]+"."+temp[5];
      runningScript = false;
      System.out.println("aaaaa");
  }

  /**
   * Used to process the request that came from the client to the server. There
   * are many things that need to be checked and verified during the processing
   * of a request. You will need to check for authentication, errors, cgi
   * scripts, type of request, etc, and handle each condition appropriately.
   *
   * HINT: it helps to use boolean flags throughout your code to check for the
   * various conditions that may or may not occur.
   *
   * @param myRequest Request object that was generated and stores all the
   *   request information that came in from the client
   * @param MIMETable Hashtable which contains the various mime types the
   *   server can handle.
   * @param env Environment object that contains the environment variables
   *   necessary for cgi script execution.
   */
  public void processRequest(Request myRequest, Hashtable MIMETable, Environment env) {
    version = myRequest.getVersion();

    if(!myRequest.isURIScript()){
        IncomingRequest = myRequest;
        URI = myRequest.getURI();
        outFile = new File(URI);

        //check authentication
        checkAuthentication(myRequest);

        //get file type
        FileExtension = URI.substring(URI.indexOf('.')+1, URI.length());
        if(checkMIME(MIMETable, FileExtension))
            FileExtension = getMIME(MIMETable,FileExtension);
        else
            FileExtension = "Unknown/Unknown";

        // check for most of the condiction and set status code
        if(!myRequest.getValidMethod()) { setStatus(501);}

        else if(!outFile.exists()){ setStatus(404); FileExtension = "text/html";}

        else if(myRequest.getMethod().equals("HEAD")) { setStatus(204);}

        else if (myRequest.getHeader().get("If-Modified-Since:")!=null)
        {
            String lastModDate = new Date(outFile.lastModified()).toString();
            String broswerModDate = (String)myRequest.getHeader().get("If-Modified-Since:");
            System.out.println(broswerModDate);

            if(lastModDate.equals(broswerModDate.trim()))
                setStatus(304);
        }
        else if(statusCode==0) { System.out.println("20000");setStatus(200);}
    }

    else{
        runningScript = true;
        CGIHandler CGI = new CGIHandler(myRequest, env);
        out = CGI.runScript();
        setStatus(200);
    }
  }

  /**
   * Used to output a correctly formatted response to the client. This function
   * will need to process any output from a cgi script as well as generate the
   * appropriate headers and body required by an HTTP response.
   *
   * @param output OutputStream object that will be used to send the response
   *   back to the socket.
   */
  public void writeOutput(OutputStream output) {
     // System.out.println("bbbbbbbbb");
      try{
        if(!runningScript){
        PrintWriter writer=new PrintWriter(output,true);

        FileInputStream in = new FileInputStream(outFile);

        outFileSize = in.available();

          writer.println(firstOutputLine);
          writer.println("DATE:"+date);
          writer.println("SERVER: Super!");
          writer.println("Content-Type: " + FileExtension);
          writer.println("Content-Length: "+ outFileSize);
          if(statusCode!=304)
          writer.println("Last-Modified: " + new Date(outFile.lastModified()).toString());
          if (moreHeaders.size()!=0)
            for(int i = 0;i<moreHeaders.size();i++)
               writer.println(moreHeaders.get(i));
         writer.println("");
        //moreHeaders.clear();
          if (statusCode!=204 && statusCode!=501)
           {
              byte[] buffer=new byte[in.available()];
              int byteReader=0;

              while((byteReader=in.read(buffer))!=-1)
                output.write(buffer, 0, byteReader);
           }

        output.flush();
        output.close();
        in.close();
        writer.close();
        }
        
        else{
            PrintWriter writer=new PrintWriter(output,true);
            writer.println(firstOutputLine);


            BufferedInputStream out1 = new BufferedInputStream(out);
            byte[] buffer = new byte[1];
            int bytesRead = 0;
            boolean notyet=true;
            while ((bytesRead = out1.read(buffer)) != -1) {
                String chunk = new String(buffer, 0, bytesRead);
                if(notyet)
                   writer.print(chunk);
                else
                   output.write(buffer, 0, bytesRead);
                if(chunk.equals("\n"))
                {
                    writer.println("");
                    notyet= false;
                }
            }
            output.flush();
            output.close();
        }
    }catch(IOException e){
        System.err.println("Error: " + e.getMessage());
    }
  }

  /**
   * Used to test for authentication. If the .htaccess file shows that
   * authentication is needed for access to the file or directory then set the
   * appropriate headers and set the appropriate status codes unless the user
   * has included their authentication. If this is the case, check to make sure
   * their authentication is valid.
   *
   * @param req Request object which is needed to check for authentication
   */
  public void checkAuthentication(Request req){
      Vector Authentication = req.getAuth();
         ForAuth currentAuthPath=null;
         String tempURI,Authrequire="",userFile="";

         for (int i =0;i<Authentication.size();i++)
         {
            currentAuthPath = (ForAuth) Authentication.get(i);
            tempURI = URI.replace("/", "\\\\");
            if(tempURI.contains(currentAuthPath.getDirectory()))
             {
                 setStatus(401);
                 userFile = currentAuthPath.getAuthUserFile();
                 Authrequire = currentAuthPath.getRequire();
                 moreHeaders.add("WWW-Authenticate: "+ currentAuthPath.getAuthType());
                 break;
             }

         }

    if(req.getHeader().get("Authorization:")!=null)
    {
        setStatus(403);
        String pwEncode = (String) req.getHeader().get("Authorization:");
        StringTokenizer Analysis = new StringTokenizer(pwEncode);
        Analysis.nextToken();
        pwEncode = Analysis.nextToken();

        if (authentication.checkAuth(pwEncode,userFile,Authrequire))
        {
            setStatus(200);
            System.out.println("whatthe");            
        }
        moreHeaders.remove(moreHeaders.size()-1);
    }

  }

  /**
   * Used to set the reason for each HTTP status code as designated by the
   * protocol.
   *
   * @param code int value which corresponds to each status code
   */
  public void setStatus(int code) {
      statusCode = code;

      if(code == 200){firstOutputLine = version + " " + statusCode + " OK";
                      outFile = new File(URI);}
      if(code == 201){firstOutputLine = version + " " + statusCode + " Created";}
      if(code == 204){firstOutputLine = version + " " + statusCode + " No Content";}
      if(code == 304){firstOutputLine = version + " " + statusCode + " Not Modified";}
      if(code == 400){firstOutputLine = version + " " + statusCode + " Bad Request";}
      if(code == 401){firstOutputLine = version + " " + statusCode + " Unauthorized";
                     outFile = new File("C:\\csc667\\java_webserver\\error\\401.html");}
      if(code == 403){firstOutputLine = version + " " + statusCode + " Forbidden";
                     outFile = new File("C:\\csc667\\java_webserver\\error\\403.html");}
      if(code == 404){firstOutputLine = version + " " + statusCode + " Not Found";
                     outFile = new File("C:\\csc667\\java_webserver\\error\\404.html");}
      if(code == 500){firstOutputLine = version + " " + statusCode + " Internal Server Error";}
      if(code == 501){firstOutputLine = version + " " + statusCode + " Not Implemented";}
  }

  /**
   * Private function used to return the appropriate mime type for the file that
   * is being requested
   *
   * @param MIMETable Hashtable of mime types from your mime.types file
   * @param extension String value which designates the extension of the file
   *   being requested. This will be used to determine the mime type
   * @return String value that contains the mime type of the file
   */
  private String getMIME(Hashtable MIMETable, String extension) {
    return (String) MIMETable.get(extension);
  }

  /**
   * Private function used to determine whether the mime type requested is a
   * valid mime type
   *
   * @param MIMETable Hashtable value of the available mime types as designated
   *   by the mime.types file
   * @param extension String value which consists of the extension type
   *   requested. Used to determine the correct mime type
   * @return true if mime type if valid, false otherwise
   */
  private boolean checkMIME(Hashtable MIMETable, String extension) {
    if(MIMETable.containsKey(extension))
        return true;
    return false;
  }

  /**
   * private function used when processing a request from the client. Here, you
   * will check for mime type validity and handle a put request if it is
   * requested. If the request is PUT, you will need to use the body of the
   * request to modify the existing file.
   *
   * @param MIMETable Hashtable that contains the valid mime types as
   *   determined by the mime.types file
   * @param body String value that contains the body of the request.
   */
  private void processWithExistence(Hashtable MIMETable, String body) {

  }

  /**
   * Private function specifically used to handle output from a cgi script. You
   * will need to check the header passed back from the cgi script to determine
   * the status code of the response. From there, add your headers, attach the
   * body and add any other server directives that need to be included.
   *
   * @param dataOut BufferedOutputStream object that will write to the client
   */
  private void processCGI(BufferedOutputStream dataOut) {

   }

  /**
   * Used to write the appropriate information to the log file.
   *
   * @param logPath String value which contains the location of your log file
   * @param host String value that contains the address of the client who made
   *   the request
   */
  public void writeToLog(String logPath, String host) {
     // System.out.println("Exe");
    try{
       String logLine = host+" - - [";
       String[] temp = time.toString().split(" ");
       logLine = logLine + temp[2]+"/"+temp[1]+"/"+temp[5]+":"+temp[3]+ "] ";
       logLine = logLine + "\"" + IncomingRequest.getFirstLine()+"\" " + statusCode +" "+outFileSize;
       
       PrintWriter outputStream = new PrintWriter(new FileOutputStream(logPath,true));
       //System.out.println("Exe");
       outputStream.println(logLine);
       //System.out.println("Exe");
       outputStream.close();
    }catch (Exception e){//Catch exception if any
      System.err.println("Errordfsdf: " + e.getMessage());
    }  
  }
}
